package org.tang.gateway.config;

import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import org.springframework.cloud.gateway.route.RouteDefinition;
import org.springframework.cloud.gateway.route.RouteDefinitionLocator;
import org.springframework.cloud.netflix.ribbon.SpringClientFactory;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.util.CollectionUtils;

import java.net.URI;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;

/**
 * @author Tang
 * @classname RibbonApplicationContextInitializer
 * @description [ 预先初始化所有的 ribbon client ]
 * ContextRefreshedEvent 要优于 ServletWebServerInitializedEvent（nacos注册开始的事件）
 * @date 2022/9/25 23:04
 */
public class RibbonApplicationContextInitializer implements ApplicationListener<ContextRefreshedEvent> {

    private final SpringClientFactory springClientFactory;

    private final Set<String> clientNames;

    private static final AtomicBoolean STARTED = new AtomicBoolean(false);

    public RibbonApplicationContextInitializer(SpringClientFactory springClientFactory,
                                               RouteDefinitionLocator routeDefinitionLocator,
                                               RibbonWatchClients ribbonWatchClients) {
        this.springClientFactory = springClientFactory;
        this.clientNames = Sets.newHashSet();
        // 是否初始化所有的
        if (ribbonWatchClients.isAllClient()) {
            routeDefinitionLocator.getRouteDefinitions()
                    .map(RouteDefinition::getUri)
                    .map(URI::toString)
                    .filter(uri -> uri.startsWith("lb"))
                    .map(uri -> uri.substring(5))
                    .subscribe(clientNames::add);
        } else {
            this.clientNames.addAll(ribbonWatchClients.getIncludeClients());
        }
    }

    protected void initialize() {
        if (!CollectionUtils.isEmpty(this.clientNames)) {
            // 提前初始化 所有的 ribbon client
            new org.springframework.cloud.netflix.ribbon.RibbonApplicationContextInitializer(
                    springClientFactory,
                    Lists.newArrayList(this.clientNames)
            ).onApplicationEvent(null);
        }
    }

    @Override
    public void onApplicationEvent(ContextRefreshedEvent event) {
        // ContextRefreshedEvent 父子容器的关系会触发多次，这里要保证一次
        if (STARTED.compareAndSet(false, true)) {
            initialize();
        }
    }

}